const { RandomForestClassifier } = require('ml-random-forest')
const path = require('path')
const ROOT_DIR = path.join(__dirname, '..')
const HELPERS_DIR = path.join(ROOT_DIR, 'helpers')
const { readAllValues } = require(path.join(HELPERS_DIR, 'readAllValues.js'))
const historicalData = readAllValues('m') // 读取数据

// 加权频率统计
const weightedFrequencyMap = {}
const weightFactor = 1.2 // 权重因子，越大，越倾向于最近的数据

// 根据历史数据计算加权频率
historicalData.forEach((record, index) => {
  const number = record.number
  const weight = Math.pow(weightFactor, historicalData.length - index - 1) // 越靠近最近的数据权重越大

  if (weightedFrequencyMap[number]) {
    weightedFrequencyMap[number] += weight
  } else {
    weightedFrequencyMap[number] = weight
  }
})

// 排序号码，出现频率加权后从高到低
const weightedSortedNumbers = Object.entries(weightedFrequencyMap).sort((a, b) => b[1] - a[1])

// 获取出现频率最高的前24个号码
const weightedTop24Numbers = weightedSortedNumbers.slice(0, 24).map((entry) => entry[0])
console.log('加权频率排名前24的号码:', weightedTop24Numbers)

// 随机森林特征准备
const createFeatures = (historicalData, sequenceLength = 5) => {
  const X = []
  const y = []

  for (let i = 0; i < historicalData.length - sequenceLength; i++) {
    const features = []
    for (let j = 0; j < sequenceLength; j++) {
      features.push(parseInt(historicalData[i + j].number, 10))
    }
    X.push(features)
    y.push(parseInt(historicalData[i + sequenceLength].number, 10)) // 下一个号码作为标签
  }

  return { X, y }
}

// 创建特征和标签
const { X, y } = createFeatures(historicalData)

// 构建随机森林分类器
const classifier = new RandomForestClassifier({
  nEstimators: 100, // 设定森林的树木数量
  maxDepth: 10, // 设置树的最大深度
  minSamplesSplit: 2 // 设置分裂一个节点所需的最小样本数
})

// 训练随机森林模型
classifier.train(X, y)

// 使用随机森林模型进行预测
const predictNextNumberWithRandomForest = (sequence) => {
  const prediction = classifier.predict([sequence])
  return prediction[0]
}

// 假设我们选择最后5期号码作为输入进行预测
const lastSequence = historicalData.slice(-5).map((record) => parseInt(record.number, 10))

const randomForestPrediction = predictNextNumberWithRandomForest(lastSequence)
console.log('随机森林预测的下一个号码:', randomForestPrediction)

// LSTM模型部分
const tf = require('@tensorflow/tfjs-node')

// LSTM模型数据准备：增加维度使得它符合LSTM的输入要求
const prepareDataForLSTM = (historicalData) => {
  const data = historicalData.map((record) => parseInt(record.number, 10)) // 转化为数字数组
  const sequenceLength = 10 // 使用前10期数据来预测下一期

  const X = []
  const y = []

  for (let i = 0; i < data.length - sequenceLength; i++) {
    X.push(data.slice(i, i + sequenceLength))
    y.push(data[i + sequenceLength])
  }

  // 将 X 转换为 3D 张量：每个时间步只有一个特征（号码）
  const X_reshaped = tf.tensor3d(
    X.map((item) => item.map((val) => [val])),
    [X.length, sequenceLength, 1]
  ) // 每个时间步一个特征
  const y_reshaped = tf.tensor2d(y, [y.length, 1])

  return { X: X_reshaped, y: y_reshaped }
}

// LSTM模型构建
const buildLSTMModel = () => {
  const model = tf.sequential()
  model.add(
    tf.layers.lstm({
      units: 50,
      inputShape: [10, 1], // 10个历史数据点，1个特征（号码）
      returnSequences: false
    })
  )
  model.add(tf.layers.dense({ units: 1 }))
  model.compile({ loss: 'meanSquaredError', optimizer: 'adam' })

  return model
}

// 数据准备
const { X: X_LSTM, y: y_LSTM } = prepareDataForLSTM(historicalData)

// 构建模型
const model = buildLSTMModel()

// 训练模型
model.fit(X_LSTM, y_LSTM, { epochs: 50, batchSize: 32 }).then(() => {
  // 预测下一期
  // 这里修改了数据的形状，确保它是三维的
  const lastSequence = historicalData.slice(-10).map((record) => parseInt(record.number, 10))
  // 确保每个时间步的数据是 [val]，转换成符合 LSTM 输入要求的三维数组
  const reshapedInput = tf.tensor3d([lastSequence.map((val) => [val])], [1, 10, 1]) // 1 batch, 10 time steps, 1 feature

  // 使用修改后的数据进行预测
  const prediction = model.predict(reshapedInput)
  prediction.print() // 打印预测的结果
})

// 多模型组合
const combinePredictions = (weightedTop24Numbers, randomForestPrediction, lstmPrediction) => {
  const combinedPrediction = []
  const numPredictions = 24
  for (let i = 0; i < numPredictions; i++) {
    const predictionSet = [
      weightedTop24Numbers[i],
      randomForestPrediction,
      lstmPrediction[i] // 假设LSTM返回的预测值
    ]
    const mostCommonPrediction = predictionSet.reduce((acc, val) => {
      acc[val] = (acc[val] || 0) + 1
      return acc
    }, {})
    const predictedNumber = Object.keys(mostCommonPrediction).reduce((a, b) =>
      mostCommonPrediction[a] > mostCommonPrediction[b] ? a : b
    )
    combinedPrediction.push(predictedNumber)
  }

  return combinedPrediction
}

// 假设LSTM模型返回的预测（这里模拟返回的预测）
const lstmPrediction = [...Array(24).keys()].map((i) => Math.floor(Math.random() * 49)) // 模拟LSTM返回的结果

// 组合多个模型的预测结果
const finalPrediction = combinePredictions(weightedTop24Numbers, randomForestPrediction, lstmPrediction)
console.log('组合后的预测结果:', finalPrediction)
